﻿using System;
using System.Collections.Generic;
using System.Data.Spatial;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;

namespace WAPISF
{
    public class JsonSpatialConverter : JsonConverter
    {

        public override bool CanConvert(Type objectType)
        {

            if (objectType == typeof(System.Data.Spatial.DbGeometry))
                return true;
            else
                return false;
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }
        /// <summary>
        /// The geojson spec is here : http://geojson.org
        /// Info on the SQL Spatial dbgeometry is here : http://msdn.microsoft.com/en-us/library/system.data.spatial.dbgeometry.aspx
        /// </summary>
        /// <param name="writer"></param>
        /// <param name="value"></param>
        /// <param name="serializer"></param>
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            DbGeometry geometry = value as DbGeometry;
            writer.WriteStartObject();
            writer.WritePropertyName("type");
            writer.WriteValue(geometry.SpatialTypeName);
            writer.WritePropertyName("coordinates");
            WriteGeometryVerticies(writer, geometry);
            writer.WriteEndObject();
        }

        private void WriteGeometryVerticies(JsonWriter writer, DbGeometry geometry)
        {
            switch (geometry.SpatialTypeName)
            {
                case "Point":
                    WritePoint(writer, geometry);
                    break;
                case "LineString":
                    WriteLine(writer, geometry);
                    break;
                case "Polygon":
                    WritePolygon(writer, geometry);
                    break;
                case "MultiPoint":
                    WriteMultiPoint(writer, geometry);
                    break;
                case "MultiLineString":
                    WriteMultiLine(writer, geometry);
                    break;
                case "MultiPolygon":
                    WriteMultiPolygon(writer, geometry);
                    break;
                case "GeometryCollection":
                    WriteGeometryCollection(writer, geometry);
                    break;
                default:
                    throw new NotImplementedException();
                    break;
            }
        }

        public void WriteCrs(JsonWriter writer, DbGeometry geometry)
        {
            writer.WritePropertyName("crs");
            writer.WriteStartObject();
            writer.WritePropertyName("type");
            writer.WriteValue("name");
            writer.WritePropertyName("properties");
            writer.WriteStartObject();
            writer.WritePropertyName("name");
            writer.WriteValue(String.Format("{0}:{1}", "EPSG", geometry.CoordinateSystemId));
            writer.WriteEndObject();
            writer.WriteEndObject();
        }

        private void WritePoint(JsonWriter writer, DbGeometry geometry)
        {
            writer.WriteStartArray();
            writer.WriteValue(geometry.XCoordinate);
            writer.WriteValue(geometry.YCoordinate);
            writer.WriteEndArray();
        }

        private void WriteMultiPoint(JsonWriter writer, DbGeometry geometry)
        {
            writer.WriteStartArray();

            int lnpoints = (int)geometry.PointCount;
            for (int i = 1; i <= lnpoints; i++)
            {
                writer.WriteStartArray();
                writer.WriteValue(geometry.PointAt(i).XCoordinate);
                writer.WriteValue(geometry.PointAt(i).YCoordinate);
                writer.WriteEndArray();
            }
            writer.WriteEndArray();
        }

        private void WriteLine(JsonWriter writer, DbGeometry geometry)
        {
            writer.WriteStartArray();

            int lnpoints = (int)geometry.PointCount;
            for (int i = 1; i <= lnpoints; i++)
            {
                writer.WriteStartArray();
                writer.WriteValue(geometry.PointAt(i).XCoordinate);
                writer.WriteValue(geometry.PointAt(i).YCoordinate);
                writer.WriteEndArray();
            }
            writer.WriteEndArray();
        }

        private void WriteMultiLine(JsonWriter writer, DbGeometry geometry)
        {
            writer.WriteStartArray();
            int elements = (int)geometry.ElementCount;
            for (int j = 1; j <= elements; j++)
            {
                int lnpoints = (int)geometry.ElementAt(j).PointCount;
                writer.WriteStartArray();
                for (int i = 1; i <= lnpoints; i++)
                {
                    writer.WriteStartArray();
                    writer.WriteValue(geometry.ElementAt(j).PointAt(i).XCoordinate);
                    writer.WriteValue(geometry.ElementAt(j).PointAt(i).YCoordinate);
                    writer.WriteEndArray();
                }
                writer.WriteEndArray();
            }
            writer.WriteEndArray();
        }

        private void WritePolygon(JsonWriter writer, DbGeometry geometry)
        {
            writer.WriteStartArray();

            // exterior ring
            writer.WriteStartArray();
            int expoints = (int)geometry.ExteriorRing.ElementAt(1).PointCount;
            for (int j = 1; j <= expoints; j++)
            {
                writer.WriteStartArray();
                writer.WriteValue(geometry.ExteriorRing.PointAt(j).XCoordinate);
                writer.WriteValue(geometry.ExteriorRing.PointAt(j).YCoordinate);
                writer.WriteEndArray();
            }
            writer.WriteEndArray();

            // interior rings
            int rings = (int)geometry.InteriorRingCount;
            for (int i = 1; i <= rings; i++)
            {
                writer.WriteStartArray();
                int inpoints = (int)geometry.InteriorRingAt(i).PointCount;
                for (int j = 1; j <= inpoints; j++)
                {
                    writer.WriteStartArray();
                    writer.WriteValue(geometry.InteriorRingAt(i).PointAt(j).XCoordinate);
                    writer.WriteValue(geometry.InteriorRingAt(i).PointAt(j).YCoordinate);
                    writer.WriteEndArray();
                }
                writer.WriteEndArray();
            }
            writer.WriteEndArray();
        }

        private void WriteMultiPolygon(JsonWriter writer, DbGeometry geometry)
        {
            writer.WriteStartArray();
            int elements = (int)geometry.ElementCount;
            for (int k = 1; k <= elements; k++)
            {
                writer.WriteStartArray();

                // exterior ring
                writer.WriteStartArray();
                int expoints = (int)geometry.ElementAt(k).ExteriorRing.ElementAt(1).PointCount;
                for (int j = 1; j <= expoints; j++)
                {
                    writer.WriteStartArray();
                    writer.WriteValue(geometry.ElementAt(k).ExteriorRing.PointAt(j).XCoordinate);
                    writer.WriteValue(geometry.ElementAt(k).ExteriorRing.PointAt(j).YCoordinate);
                    writer.WriteEndArray();
                }
                writer.WriteEndArray();

                // interior rings
                int rings = (int)geometry.ElementAt(k).InteriorRingCount;
                for (int i = 1; i <= rings; i++)
                {
                    writer.WriteStartArray();
                    int inpoints = (int)geometry.ElementAt(k).InteriorRingAt(i).PointCount;
                    for (int j = 1; j <= inpoints; j++)
                    {
                        writer.WriteStartArray();
                        writer.WriteValue(geometry.ElementAt(k).InteriorRingAt(i).PointAt(j).XCoordinate);
                        writer.WriteValue(geometry.ElementAt(k).InteriorRingAt(i).PointAt(j).YCoordinate);
                        writer.WriteEndArray();
                    }
                    writer.WriteEndArray();
                }
                writer.WriteEndArray();
            }
            writer.WriteEndArray();
        }

        private void WriteGeometryCollection(JsonWriter writer, DbGeometry geometry)
        {
            throw new NotImplementedException();
        }
    }
}
